/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package digitaldreamers.games.ddmonsters.servers.gameserver.components;

import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SSummonMonsterRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SPlayerLeaveRoomPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SLoginRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SStartDuelRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SSetReadyStateRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SOpponentSummonMonsterPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.STurnChangedPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SLeaveRoomRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.STurnPhaseChangedPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SJoinRoomRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SCardsDrawnPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SCreateRoomRequestResponse;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SPlayerIsReadyPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SDuelStartedPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SPlayerJoinRoomPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SDuelEndedPacket;
import digitaldreamers.games.ddmonsters.servers.gameserver.network.packets.sending.SGetRoomListRequestResponse;
import digitaldreamers.libraries.games.servers.networkmanagement.abstracts.IConnectionHandler;
import digitaldreamers.libraries.games.servers.networkmanagement.abstracts.IPlayerComponent;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelCellComponent.Coordinates;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelComponent.IOnDuelEndListener;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelComponent.IOnDuelStartListener;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelComponent.IOnTurnChangedListener;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelComponent.IOnTurnPhaseChangedListener;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.DuelComponent.TurnPhases;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.RoomComponent.FullSubstates;
import digitaldreamers.games.ddmonsters.servers.gameserver.components.basicinfomation.PlayerInformation;
import digitaldreamers.games.ddmonsters.servers.gameserver.data.FacadeDataHandler;
import digitaldreamers.games.ddmonsters.servers.gameserver.DDMonsterGameServer;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 *
 * @author DaoLe
 */
public class PlayerComponent implements IPlayerComponent, IOnDuelStartListener, IOnDuelEndListener, IOnTurnPhaseChangedListener, IOnTurnChangedListener {
    // <editor-fold defaultstate="collapsed" desc="Constants">
    private static final String ERROR_MESSAGE__INVALID_NUMBER_OF_CARDS_TO_DRAW = "Number of cards to drawn must be greater than 0.";
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Inner classes">
    public static enum PlayerStates { 
        CONNECTED, 
        NOT_IN_ROOM, 
        IN_ROOM 
    } //Player states.
    
    public static enum InRoomSubstates { 
        WAITING, 
        CLIENT_NOT_READY,
        CLIENT_READY, 
        DUELING 
    } //Substates of the IN_ROOM state when the 2 players start the duel.
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Members">
    private PlayerInformation mPlayerInformation;
    private PlayerStates mPlayerState;
    private InRoomSubstates mInRoomSubstate;
    private IConnectionHandler mConnectionHandler;
    private FacadeDataHandler mDataHandler;
    private RoomComponent mRoom;
    private DuelComponent mDuel;
    private DeckComponent mActiveDeck;
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Get/Set methods">
    /**
     * Get the current state of the player.
     * @return The current state of the player.
     */
    public PlayerStates getState() {
        return mPlayerState;
    }
    
    /**
     * Get the substate of the player when he has joined a room.
     * @return The current substate of the player when he has joined a room.
     */
    public InRoomSubstates getInRoomSubstate() {
        if(mPlayerState == PlayerStates.IN_ROOM) {
            return mInRoomSubstate;
        }
        return null;
    }
    
    /**
     * Get the room that the player is in.
     */
    public RoomComponent getRoom()
    {
        return mRoom;
    }
    
    /**
     * Set the room the player is in.
     * @param room The room the player is in.
     */
    public void setRoom(RoomComponent room)
    {
        this.mRoom = room;
        if(mRoom == null) {
            mPlayerState = PlayerStates.NOT_IN_ROOM;
            mInRoomSubstate = null;
        }
        else {
            mPlayerState = PlayerStates.IN_ROOM;
            mInRoomSubstate = InRoomSubstates.WAITING;
        }
    }
    
    /**
     * Get the connection handler of this player.
     * @return The connection handler.
     */
    public IConnectionHandler getConnectionHandler() {
        return mConnectionHandler;
    }
    
    /**
     * Get player information.
     * @return Player information.
     */
    public PlayerInformation getPlayerInformation() {
        return mPlayerInformation;
    }
    
    /**
     * Get the duel that this player is playing in.
     * @return The duel that this player is playing in.
     */
    public DuelComponent getDuel() {
        return mDuel;
    }
    
    /**
     * Set the duel that this player will play in.
     * @param duel The duel that this player will play in.
     */
    public void setDuel(DuelComponent duel) {
        if(duel == null) {
            if(mDuel != null) {
                mInRoomSubstate = InRoomSubstates.WAITING;
                mDuel.removeDuelStartListener(this);
                mDuel.removeDuelEndListener(this);
                mDuel.removeTurnPhaseChangedListener(this);
                mDuel.removeTurnChangedListener(this);
                mDuel = duel;
            }
        }
        else {
            if(mDuel == null) {
                mInRoomSubstate = InRoomSubstates.CLIENT_NOT_READY;
                duel.addDuelStartListener(this);
                duel.addDuelEndListener(this);
                duel.addTurnPhaseChangedListener(this);
                duel.addTurnChangedListener(this);
                mDuel = duel;
            }
        }
    }
    
    public DeckComponent getActiveDeck() {
        return mActiveDeck;
    }
    
    public void setActiveDeck(DeckComponent activeDeck) {
        mActiveDeck = activeDeck;
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Constructors">
    public PlayerComponent(IConnectionHandler pConnectionHandler) {
        mPlayerState = PlayerStates.CONNECTED;
        mConnectionHandler = pConnectionHandler;
        mDataHandler = FacadeDataHandler.getInstance() ;
    }
    
    public void loginByUiid(String pUiid) throws SQLException {
        try {
            boolean loginResult = false;
            mPlayerInformation = mDataHandler.getPlayerByUiid(pUiid);
            if(mPlayerInformation != null) {
                loginResult = true;
                mPlayerState = PlayerStates.NOT_IN_ROOM;
            }
            mConnectionHandler.send(new SLoginRequestResponse(loginResult, mPlayerInformation));
        } catch (SQLException ex) {
            throw ex;
        }
    }
    
    public void loginByLoginAndPassword(String pLogin, String pPassword) throws SQLException {
        try {
            boolean loginResult = false;
            mPlayerInformation = mDataHandler.getPlayerByLoginAndPassword(pLogin, pPassword);
            if(mPlayerInformation != null) {
                loginResult = true;
                mPlayerState = PlayerStates.NOT_IN_ROOM;
            }
            mConnectionHandler.send(new SLoginRequestResponse(loginResult, mPlayerInformation));
        } catch (SQLException ex) {
            throw ex;
        }
    }
    
    public void getRoomList() {
        RoomComponent[] roomList = ((DDMonsterGameServer)mConnectionHandler.getGameServer()).getRoomList();
        mConnectionHandler.send(new SGetRoomListRequestResponse(roomList));
    }
    
    public void createRoom(/*RoomRules roomRule*/) {
        boolean bResult = RoomComponent.createRoom(this/*, roomRule*/);
        mConnectionHandler.send(new SCreateRoomRequestResponse(bResult));
    }
    
    public void joinRoom(long lRoomID) {
        RoomComponent theRoom = ((DDMonsterGameServer) mConnectionHandler.getGameServer()).getRoomById(lRoomID);
        if (theRoom != null) {
            boolean bIsJoinable = theRoom.joinRoom(this);
            mConnectionHandler.send(new SJoinRoomRequestResponse(bIsJoinable, theRoom));
            if (bIsJoinable) {
                PlayerComponent roomMaster = theRoom.getRoomMaster();
                if (roomMaster != null) {
                    roomMaster.getConnectionHandler().send(new SPlayerJoinRoomPacket(this));
                }
            }
        }
    }
    
    /*
    public void changeRoomRule(RoomRules roomRule) {
        try {
            mRoom.setRoomRule(roomRule);
            SChangeRoomRuleRequestResponse response = new SChangeRoomRuleRequestResponse(roomRule.getRule());
            connectionHandler.send(response.toByteArray());
            if(getRoom().getRoomGuest() != null) {
                getRoom().getRoomGuest().getConnectionHandler().send(response.toByteArray());
            }
        } catch (IOException ex) {
            throw ex;
        }
    }
    */
    
    public void leaveRoom(boolean bIsDisconnected) {
        RoomComponent theRoom = mRoom;
        boolean bResult = mRoom.leaveRoom(this);
        if (bResult) {
            if (!bIsDisconnected) {
                mConnectionHandler.send(new SLeaveRoomRequestResponse(bResult));
            }
            if (theRoom.getRoomState() != RoomComponent.RoomStates.REMOVED) {
                PlayerComponent roomMaster = theRoom.getRoomMaster();
                if (roomMaster != null) {
                    roomMaster.getConnectionHandler().send(new SPlayerLeaveRoomPacket(this));
                }
            }
        }
    }
    
    public void ready(boolean bReady) {
        boolean bResult = mRoom.ready(this, bReady);
        mConnectionHandler.send(new SSetReadyStateRequestResponse(bResult));
        if (bResult) {
            PlayerComponent roomMaster = mRoom.getRoomMaster();
            if (roomMaster != null) {
                roomMaster.getConnectionHandler().send(new SPlayerIsReadyPacket(bReady));
            }
        }
    }
    
    public void startDuel() {
        boolean bResult = mRoom.startDuel(this);
        if (bResult) {
            SStartDuelRequestResponse reply = new SStartDuelRequestResponse(true);
            mConnectionHandler.send(reply);
            PlayerComponent roomGuest = mRoom.getRoomGuest();
            if (roomGuest != null) {
                roomGuest.getConnectionHandler().send(reply);
            }
        }
    }
    
    public void playerClientIsReady() {
        if(mRoom.getFullSubstate() == FullSubstates.PLAYING && mInRoomSubstate == InRoomSubstates.CLIENT_NOT_READY) {
            mInRoomSubstate = InRoomSubstates.CLIENT_READY;
            mDuel.playerClientIsReady(this);
        }
    }
    
    public void prepareForDuel() {
        if(mActiveDeck == null) {
            try {
                mActiveDeck = new DeckComponent(1, this, mDataHandler);
                mActiveDeck.loadCards();
            } catch (SQLException ex) {
                mConnectionHandler.fireConnectionErrorEvent();
            }
        }
        mActiveDeck.prepareForDuel();
    }
    
    public void drawCardsAtDuelStart() {
        drawCards(PlayerDuelStatusComponent.NUMBER_OF_CARDS_DRAWN_AT_START);
    }
    
    public void drawCard() {
        drawCards(1);
    }
    
    private boolean drawCards(int iNumber) throws IllegalArgumentException {
        /*
        if(iNumber <= 0) {
            throw new IllegalArgumentException(ERROR_MESSAGE__INVALID_NUMBER_OF_CARDS_TO_DRAW);
        }
        boolean bResult = true;
        ArrayList<PlayerCardComponent> drawnCards = new ArrayList<>(iNumber);
        for(int i = 0; i < iNumber; i++) {
            if(m_PlayerDuelStatus.getNumberOfCardsInHand() < MAX_NUMBER_OF_CARDS_IN_HAND) {
                PlayerCardComponent card = mActiveDeck.drawCard();
                if(card == null) {
                    bResult = false;
                    break;
                }
                drawnCards.add(card);
                m_PlayerDuelStatus.addCardToHand(card);
            }
            else {
                break;
            }
        }
        */
        ArrayList<PlayerCardComponent> drawnCards = mDuel.drawCards(this, iNumber);
        mConnectionHandler.send(new SCardsDrawnPacket(drawnCards));
        return true;
        //return bResult;
    }
    
    public void summonMonster(int iHandIndex, int iSummonCoordinateX, int iSummonCoordinateY) {
        /*
        try {
            boolean bResult;
            if(mDuel.isTurnPlayer(this)) {
                PlayerCardComponent card = m_PlayerDuelStatus.getCardInHandAtIndex(iHandIndex);
                if(card != null && card instanceof MonsterCardComponent) {
                    MonsterCardComponent monsterCard = (MonsterCardComponent)card;
                    bResult = mDuel.summonMonster(this, monsterCard, iSummonCoordinateX, iSummonCoordinateY);
                    mConnectionHandler.send(new SSummonMonsterRequestResponse(bResult, iHandIndex, iSummonCoordinateX, iSummonCoordinateY).toByteArray());
                    if(bResult) {
                        m_PlayerDuelStatus.removeCardFromHandByIndex(iHandIndex);
                        Coordinates summonCoordinates = new Coordinates(iSummonCoordinateX, iSummonCoordinateY);
                        DuelFieldComponent.reverseCoordinates(summonCoordinates);
                        mRoom.getOpponent(this).getConnectionHandler().send(new SOpponentSummonMonsterPacket(iHandIndex, summonCoordinates.getX(), summonCoordinates.getY()).toByteArray());
                    }
                }
            }
        } catch (IOException ex) {
            throw ex;
        }
        */
        boolean bResult = mDuel.summonMonster(this, iHandIndex, iSummonCoordinateX, iSummonCoordinateY);
        mConnectionHandler.send(new SSummonMonsterRequestResponse(bResult, iHandIndex, iSummonCoordinateX, iSummonCoordinateY));
        if (bResult) {
            Coordinates summonCoordinates = new Coordinates(iSummonCoordinateX, iSummonCoordinateY);
            DuelFieldComponent.reverseCoordinates(summonCoordinates);
            mRoom.getOpponent(this).getConnectionHandler().send(new SOpponentSummonMonsterPacket(iHandIndex, summonCoordinates.getX(), summonCoordinates.getY()));
        }
    }
    
    @Override
    public void onDuelStart(PlayerComponent turnPlayer) {
        mInRoomSubstate = InRoomSubstates.DUELING;
        mConnectionHandler.send(new SDuelStartedPacket(this == turnPlayer ? true : false));
        drawCardsAtDuelStart();
    }

    @Override
    public void onTurnPhaseChanged(PlayerComponent turnPlayer, TurnPhases currentPhase) {
        mConnectionHandler.send(new STurnPhaseChangedPacket(this == turnPlayer ? true: false, currentPhase.getPhase()));
    }

    @Override
    public void onTurnChanged(PlayerComponent turnPlayer) {
        mConnectionHandler.send(new STurnChangedPacket(this == turnPlayer ? true: false));
    }
    
    @Override
    public void onDuelEnd(PlayerComponent winner) {
        mConnectionHandler.send(new SDuelEndedPacket(this == winner ? true: false));
    }
}
